package global

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	glog "gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"longmen/server/pkg/db/mysql/models/models_report"
	"moul.io/zapgorm2"
	"time"
)

func InitDB() error {
	fName := "initDB"
	err := InitMasterEngine(
		Config.GetString("mysql.host"),
		Config.GetString("mysql.username"),
		Config.GetString("mysql.password"),
		Config.GetString("mysql.database"),
		Config.GetInt("mysql.port"),
	)
	if err != nil {
		log.Fatal("%s InitMasterEngine fail %s", fName, err.Error())
	}

	err = InitSlaverEngine(
		Config.GetString("mysql_slave.host"),
		Config.GetString("mysql_slave.username"),
		Config.GetString("mysql_slave.password"),
		Config.GetString("mysql_slave.database"),
		Config.GetInt("mysql_slave.port"),
	)
	if err != nil {
		log.Fatal("%s InitSlaverEngine fail %s", fName, err.Error())
	}

	err = InitSportDb(
		Config.GetString("mysql_sport.host"),
		Config.GetString("mysql_sport.username"),
		Config.GetString("mysql_sport.password"),
		Config.GetString("mysql_sport.database"),
		Config.GetInt("mysql_sport.port"),
	)
	if err != nil {
		log.Fatal("%s InitSportDb fail %s", fName, err.Error())
	}

	err = InitSportSharding(
		Config.GetString("mysql_sport.host"),
		Config.GetString("mysql_sport.username"),
		Config.GetString("mysql_sport.password"),
		Config.GetString("mysql_sport.database"),
		Config.GetInt("mysql_sport.port"),
	)
	if err != nil {
		log.Fatal("%s InitSportSharding fail %s", fName, err.Error())
	}

	err = InitLiveDb(
		Config.GetString("mysql_live.host"),
		Config.GetString("mysql_live.username"),
		Config.GetString("mysql_live.password"),
		Config.GetString("mysql_live.database"),
		Config.GetInt("mysql_live.port"),
	)
	if err != nil {
		log.Fatal("%s InitLiveEngine fail %s", fName, err.Error())
	}

	err = InitGuessDb(
		Config.GetString("mysql_guess.host"),
		Config.GetString("mysql_guess.username"),
		Config.GetString("mysql_guess.password"),
		Config.GetString("mysql_guess.database"),
		Config.GetInt("mysql_guess.port"),
	)
	if err != nil {
		log.Fatal("%s InitGuessEngine fail %s", fName, err.Error())
	}
	err = InitReportDb(
		Config.GetString("mysql_report.host"),
		Config.GetString("mysql_report.username"),
		Config.GetString("mysql_report.password"),
		Config.GetString("mysql_report.database"),
		Config.GetInt("mysql_report.port"),
	)
	if err != nil {
		log.Fatal("%s InitGuessEngine fail %s", fName, err.Error())
	}
	return nil
}

var x *gorm.DB
var r *gorm.DB
var sport *gorm.DB
var sportSharding *gorm.DB
var live *gorm.DB
var guess *gorm.DB
var report *gorm.DB

var DsnTmp = "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local"
var LockForUpdate = clause.Locking{
	Strength: "UPDATE",
}

func getDB(dsn, dbName string) (*gorm.DB, error) {
	logger := zapgorm2.New(InitGormLogger(dbName))
	logger.LogLevel = glog.LogLevel(4)
	logger.SetAsDefault() // optional: configure gorm to use this zapgorm.Logger for callbacks
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger,
	})
	Log.Debugf("DB add: %s", dsn)
	if err != nil {
		return nil, fmt.Errorf("connect database error: %s", err.Error())
	}
	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("get sql db is error: %+v", err.Error())
	}
	sqlDB.SetMaxIdleConns(30)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Minute)
	if err = sqlDB.Ping(); err != nil {
		return nil, fmt.Errorf("db ping is err: %+v", err.Error())
	}
	return db, nil
}
func getSportDB(dsn, dbName string) (*gorm.DB, error) {
	logger := zapgorm2.New(InitGormLogger(dbName))
	logger.LogLevel = glog.LogLevel(4)
	logger.SetAsDefault() // optional: configure gorm to use this zapgorm.Logger for callbacks
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger,
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "db_", // 表前缀
			SingularTable: true,  // 单数表名称
			NoLowerCase:   false, // snake_case 转换
		},
	})
	Log.Debugf("DB add: %s", dsn)
	if err != nil {
		return nil, fmt.Errorf("connect database error: %s", err.Error())
	}
	return db, nil
}

func InitSportDb(host, user, password, dbname string, port int) (err error) {
	if sport != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	sport, err = getSportDB(dsn, dbname)
	if err != nil {
		return err
	}
	if err != nil {
		return err
	}
	return
}

func InitLiveDb(host, user, password, dbname string, port int) (err error) {
	if live != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	live, err = getDB(dsn, dbname)
	if err != nil {
		return err
	}
	return
}

func InitGuessDb(host, user, password, dbname string, port int) (err error) {
	if guess != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	guess, err = getDB(dsn, dbname)
	if err != nil {
		return err
	}
	return
}

func InitReportDb(host, user, password, dbname string, port int) (err error) {
	if report != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	report, err = getDB(dsn, dbname)
	if err != nil {
		return err
	}
	return
}

func InitMasterEngine(host, user, password, dbname string, port int) (err error) {
	if x != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	x, err = getDB(dsn, dbname)
	if err != nil {
		return err
	}
	x.AutoMigrate(&models_report.GameFruitFlow{})
	return
}

func InitSlaverEngine(host, user, password, dbname string, port int) (err error) {
	if r != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	r, err = getDB(dsn, dbname)
	if err != nil {
		return err
	}
	return
}

// 主库
func GetX() *gorm.DB {
	return x
}
func Sport() *gorm.DB {
	return sport
}
func Live() *gorm.DB {
	return live
}
func Guess() *gorm.DB {
	return guess
}
func Report() *gorm.DB {
	return report
}

// 从库
func GetR() *gorm.DB {
	if r == nil {
		return x
	}
	return r
}

func InitSportSharding(host, user, password, dbname string, port int) (err error) {
	if sportSharding != nil {
		return
	}
	dsn := fmt.Sprintf(DsnTmp, user, password, host, port, dbname)
	sportSharding, err = getSportDB(dsn, dbname)
	if err != nil {
		return err
	}
	//err = sportSharding.Use(sharding2.Register(sharding2.Config{
	//	ShardingKey:         "match_id",
	//	NumberOfShards:      128,
	//	PrimaryKeyGenerator: sharding2.PKSnowflake,
	//}, models_sports.FootballOdds{}, models_sports.BasketballOdds{}))
	//if err != nil {
	//	return err
	//}
	return
}

func SportSharding() *gorm.DB {
	return sportSharding
}
